package jp.ac.u_tokyo.dch.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.security.Provider;
import java.security.Security;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.xwork.StringUtils;
import org.w3c.dom.Element;

/**
 * @version 1.1
 */
public class DchCommonUtil {
	/** ロガー */
	private static DchLogger logger = DchLogger.getLogger(DchCommonUtil.class);

	/** サムネイルヘッダ */
	public static final String THUMNBNAIL_PREFIX = "thumnail_";

	/** DES_KEY */
	public static final String DES_KEY = "dchTokyo";
	/** COOKIE_NAME */
	public static final String COOKIE_NAME = "LTIMEA";
	/** COOKIE_SPILT_CHAR */
	public static final String COOKIE_SPILT_CHAR = "#dch#";

	public static final String MATCH_URL = "\\b(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";

	/**
	 * XMLデータの要素から指定属性の値を取得
	 *
	 * @param element
	 *            XMLデータ要素
	 * @param tagName
	 *            タグ名
	 * @param attribute
	 *            属性名
	 * @return 属性値
	 */
	public static String getNodeAttribute(Element element, String tagName,
			String attribute) {
		String ret = null;
		if (element.getElementsByTagName(tagName).item(0) != null) {
			ret = ((Element) element.getElementsByTagName(tagName).item(0))
					.getAttribute(attribute);
		}
		return ret;
	}

	/**
	 * XMLデータの要素から指定タグのNode値を取得
	 *
	 * @param element
	 *            要素
	 * @param tagName
	 *            タグ名
	 * @return Node値
	 */
	public static String getNodeValue(Element element, String tagName) {
		String ret = "";
		if (element.getElementsByTagName(tagName).item(0) != null
				&& element.getElementsByTagName(tagName).item(0)
						.getFirstChild() != null) {
			ret = element.getElementsByTagName(tagName).item(0).getFirstChild()
					.getNodeValue();
		}
		return ret;
	}

	/**
	 * HTMLのタグをエスケープする
	 *
	 * @param input
	 * @return
	 */
	public static String escapeHtmlTag(String input) {

		StringBuffer sb = new StringBuffer();

		if (input == null)
			return "";

		for (int i = 0; i < input.length(); i++) {
			if (input.charAt(i) == '<') {
				sb.append("&lt;");
			} else if (input.charAt(i) == '>') {
				sb.append("&gt;");
			} else if (input.charAt(i) == '"') {
				sb.append("&quot;");
			} else if (input.charAt(i) == '&') {
				sb.append("&amp;");
			} else if (input.charAt(i) == '\'') {
				sb.append("&#39;");
			} else {
				sb.append(input.charAt(i));
			}
		}

		return sb.toString();
	}

	/**
	 * 映像のThumbnailを作成
	 *
	 * @param ffmpegExe
	 *            ffmpeg実行ファイル
	 * @param source
	 *            映像ファイル
	 * @param destination
	 *            出力先
	 * @param thumbFileName
	 *            thumbnailファイル名
	 * @param width
	 *            thumbnail画像サイズ：幅
	 * @param height
	 *            thumbnail画像サイズ：縦
	 * @param outCnt
	 *            thumbnail画像数
	 * @return 処理結果
	 */
	public static String makeViedoThumbnail(String ffmpegExe, String source,
			String destination, String thumbFileName, String thumbSize,
			String thumbOutCnt) {
		List<String> command = null;
		String outFileName = "";
		try {
			if (destination == null || destination.equals(""))
				destination = "./";
			String osName = System.getProperty("os.name");
			logger.debug(osName);
			// ffmpeg -i a.flv -an -ss 00:00:00 -s 160x120 -r 0.1 -vframes 10 -y
			// %3d.jpg
			command = new ArrayList<String>(10);
			if (osName.toLowerCase().startsWith("window")) {
				command.add("cmd.exe");
				command.add("/c");
			}
			command.add(ffmpegExe);
			command.add("-i");
			command.add(source); // 元ファイル
			command.add("-an");
			command.add("-ss");
			command.add("00:00:10"); // 開始時間
			command.add("-s");
			command.add(thumbSize); // 画像サイズ
			command.add("-r");
			command.add("0.1");
			command.add("-vframes");
			command.add(thumbOutCnt); // tumnnailされる画像数
			command.add("-y");
			command.add(destination + THUMNBNAIL_PREFIX + "%3d_"
					+ thumbFileName + ".jpg"); // 出力ファイル
			logger.debug(command);
			exec((String[]) command.toArray(new String[1]));
			outFileName = THUMNBNAIL_PREFIX + "001_" + thumbFileName + ".jpg";
		} catch (Exception e) {
			logger.error(e);
		}

		return outFileName;
	}

	/**
	 * 動画の変換処理を行う
	 *
	 * @param ffmpegExe
	 *            ffmpeg実行ファイル
	 * @param source
	 *            映像ファイル
	 * @param destination
	 *            出力先
	 * @param thumbFileName
	 *            thumbnailファイル名
	 * @return 出力ファイル名
	 */
	public static String makeViedoConvert(String ffmpegExe, String source,
			String destination, String thumbFileName) {
		List<String> command = null;
		String outFileName = "";
		try {

			if (destination == null || destination.equals(""))
				destination = "./";
			String osName = System.getProperty("os.name");
			logger.info(osName);
			// ffmpeg -i sample_mpeg4.mp4 -sameq -ar 44100 -f flv output.flv
			// ffmpeg -y -i "<%InputFile%>" -bitexact -vcodec flv -fixaspect -r
			// 29.97 -b 500 -acodec mp3 -ac 2 -ar 44100 -ab 96 -f flv
			// "<%OutputFile%>.flv""
			command = new ArrayList<String>(10);
			if (osName.toLowerCase().startsWith("window")) {
				command.add("cmd.exe");
				command.add("/c");
			}

			// command.add(ffmpegExe);
			// command.add("-i");
			// command.add(source); //元ファイル
			// command.add("-sameq");//動画を圧縮せずに作成する
			// command.add("-ar"); //オーディオ
			// command.add("44100");
			// command.add("-acodec");
			// command.add("libmp3lame");
			// command.add("-ab");
			// command.add("96k");
			// command.add("-f");
			// command.add("flv");
			// command.add(destination+thumbFileName+".flv"); //出力ファイル

			command.add(ffmpegExe);
			command.add("-y");
			command.add("-i");
			command.add(source); // 元ファイル
			command.add("-vcodec");
			command.add("flv");
			command.add("-r");
			command.add("29.97");
			command.add("-qscale");
			command.add("10");
			command.add("-acodec");
			command.add("libmp3lame");
			command.add("-ar"); // オーディオ
			command.add("44100");
			command.add("-ab");
			command.add("64k");
			command.add("-f");
			command.add("flv");
			command.add(destination + thumbFileName + ".flv"); // 出力ファイル

			logger.info(command);
			int result = exec((String[]) command.toArray(new String[1]));
			if (result == 0 || result == 1) {
				// outFileName =THUMNBNAIL_PREFIX+thumbFileName+".flv";
				outFileName = thumbFileName + ".flv";
			} else {
				outFileName = "error";
			}
		} catch (Exception e) {
			logger.error(e);
		}

		return outFileName;
	}

	/**
	 * Flvtoolsを利用して変換されたFLVファイルにメタ情報を書き込む
	 *
	 * @param flvtools
	 *            実行ファイル
	 * @param source
	 *            映像ファイル
	 * @return 結果
	 */
	public static int exeFlvToos(String flvTools, String source) {
		List<String> command = null;
		int result = -1;
		try {

			if (source == null || source.equals(""))
				source = "./";
			String osName = System.getProperty("os.name");
			logger.info(osName);
			// flv
			command = new ArrayList<String>(10);
			if (osName.toLowerCase().startsWith("window")) {
				command.add("cmd.exe");
				command.add("/c");
			}
			command.add(flvTools);
			command.add("-U");
			command.add(source); // 元ファイル

			logger.info(command);
			result = exec((String[]) command.toArray(new String[1]));
		} catch (Exception e) {
			logger.error(e);
		}

		return result;
	}

	/**
	 * 画像の変換処理を行う
	 *
	 * @param imageMagickExe
	 *            imageMagick実行ファイル
	 * @param inputfile
	 *            元ファイル
	 * @param outputFile
	 *            出力ファイル
	 * @param imgSize
	 *            thumbnail画像サイズ:幅×縦
	 * @return 処理結果
	 */
	public static int execImageConvert(String imageMagickExe, String inputfile,
			String outputFile, String imgSize) {
		List<String> command = null;
		int result = -1;
		try {
			command = new ArrayList<String>(10);

			String convertImageSize = "";
			if (imgSize == null) {
				Map<String, Integer> imageSizeInfo = DchCommonUtil
						.execImageSizeCheck(inputfile);
				if (imageSizeInfo.size() == 2) {
					int width = imageSizeInfo.get("width");
					int height = imageSizeInfo.get("height");
					logger.warn("execImageConvert(元のサイズ):" + width + "x"
							+ height);
					if (width > 1920 || height > 1200) {
						convertImageSize = "1920x1200";
					}
				}
			}
			// サバー側のOSがWINDOW系の場合、CMDコマンドを追加
			String osName = System.getProperty("os.name");
			logger.info(osName);
			if (osName.toLowerCase().startsWith("window")) {
				command.add("cmd.exe");
				command.add("/c");
			}
			command.add(imageMagickExe);
			if (convertImageSize.length() > 0) {

				command.add("-geometry");
				command.add(convertImageSize);
			} else if (StringUtils.isNotEmpty(imgSize)) {
				command.add("-resize");
				command.add(imgSize);
			}

			command.add(inputfile);
			command.add(outputFile); // 出力ファイル
			logger.info(command);
			result = exec((String[]) command.toArray(new String[1]));
		} catch (Exception e) {
			logger.error(e);
		}

		return result;
	}

	/**
	 * 画像のサイズ情報を取得
	 *
	 * @param command
	 *            コマンド配列
	 * @return 処理結果
	 */
	public static Map<String, Integer> execImageSizeCheck(String inputfile) {
		Process proc = null;
		List<String> command = null;
		Map<String, Integer> sizeInfo = new HashMap<String, Integer>();
		int exitStatus = -1;
		try {
			command = new ArrayList<String>(10);
			// サバー側のOSがWINDOW系の場合、CMDコマンドを追加
			String osName = System.getProperty("os.name");
			logger.info(osName);
			if (osName.toLowerCase().startsWith("window")) {
				command.add("cmd.exe");
				command.add("/c");
			}
			command.add("identify");
			command.add(inputfile);
			proc = Runtime.getRuntime().exec(
					(String[]) command.toArray(new String[1]));
			BufferedReader ebr = new BufferedReader(new InputStreamReader(
					proc.getInputStream()));
			String imageInfo = "";
			String size[] = null;
			if ((imageInfo = ebr.readLine()) != null) {
				String[] imageInfos = imageInfo.split(" ");
				if (imageInfos != null && imageInfos.length > 3) {
					size = imageInfos[2].split("x");
					if (size != null && size.length == 2) {
						sizeInfo.put("width", new Integer(size[0]));
						sizeInfo.put("height", new Integer(size[1]));
					}

				}

			}
			ebr.close();
		} catch (Exception e) {
			logger.error(e);
		}
		while (true) {
			try {
				exitStatus = proc.waitFor();
				break;
			} catch (java.lang.InterruptedException e) {
				logger.error("Interrupted: Ignoring and waiting");
			}
		}
		if (exitStatus != 0) {
			logger.error("Error executing command: " + exitStatus);
		}
		return sizeInfo;
	}

	/**
	 * 指定されたコマンドを実行
	 *
	 * @param command
	 *            コマンド配列
	 * @return 処理結果
	 */
	public static int exec(String[] command) {
		Process proc = null;
		int exitStatus = -1;
		try {
			proc = Runtime.getRuntime().exec(command);
			BufferedReader ebr = new BufferedReader(new InputStreamReader(
					proc.getErrorStream()));
			String eline = null;
			while ((eline = ebr.readLine()) != null) {
				logger.info(eline);
			}
			ebr.close();
		} catch (Exception e) {
			logger.error(e);
			return exitStatus;
		}
		while (true) {
			try {
				exitStatus = proc.waitFor();
				break;
			} catch (java.lang.InterruptedException e) {
				logger.error("Interrupted: Ignoring and waiting");
			}
		}
		if (exitStatus != 0) {
			logger.error("Error executing command: " + exitStatus);
		}
		return exitStatus;
	}

	/**
	 * default日付形式(yyyy/MM/dd)で現在日付を取得
	 *
	 * @return 現在日付
	 */
	public static String getCurrentDate() {
		String currentDate = "";
		String format = "yyyy/MM/dd";
		currentDate = getCurrentDate(format);
		return currentDate;
	}

	/**
	 * 指定した日付形式で現在日付を取得
	 *
	 * @param formatStr
	 *            日付形式
	 * @return 現在日付
	 */
	public static String getCurrentDate(String formatStr) {
		String currentDate = "";
		String format = "yyyy/MM/dd";
		if (StringUtils.isNotEmpty(formatStr)) {
			format = formatStr;
		}
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		currentDate = sdf.format(Calendar.getInstance().getTime());
		logger.info("getCurrentDate:" + currentDate);
		return currentDate;
	}

	/**
	 * DES方式 パスワード暗号化
	 *
	 * @param plainText
	 * @return
	 * @throws Exception
	 */
	public static String doEncrypt(String plainText) throws Exception {
		String encryptedStr = "";
		Provider sunJce = new com.sun.crypto.provider.SunJCE();

		Security.addProvider(sunJce);

		Cipher c = Cipher.getInstance("DES");
		SecretKeyFactory keyFac = SecretKeyFactory.getInstance("DES");
		DESKeySpec keySpec = new DESKeySpec(DES_KEY.getBytes());
		SecretKey secKey = keyFac.generateSecret(keySpec);

		c.init(Cipher.ENCRYPT_MODE, secKey);
		byte[] b = c.doFinal(plainText.getBytes());
		encryptedStr = new sun.misc.BASE64Encoder().encode(b);
		return encryptedStr;
	}

	/**
	 * DES方式 パスワード復号化
	 *
	 * @param cipherText
	 * @return
	 * @throws Exception
	 */
	public static String doDecrypt(String cipherText) throws Exception {
		String dEncryptedStr = "";
		Provider sunJce = new com.sun.crypto.provider.SunJCE();
		Security.addProvider(sunJce);

		Cipher c = Cipher.getInstance("DES");
		SecretKeyFactory keyFac = SecretKeyFactory.getInstance("DES");
		DESKeySpec keySpec = new DESKeySpec(DES_KEY.getBytes());
		SecretKey secKey = keyFac.generateSecret(keySpec);

		byte[] b = new sun.misc.BASE64Decoder().decodeBuffer(cipherText);
		c.init(Cipher.DECRYPT_MODE, secKey);
		dEncryptedStr = new String(c.doFinal(b));
		return dEncryptedStr;
	}

	/**
	 * ログインユーザIDを取得
	 *
	 * @param request
	 *            HttpServletRequest
	 * @return ログインユーザID
	 */
	public static String getLoginIdFromRequest(HttpServletRequest request) {
		String loginId = "";
		HttpSession session = request.getSession();
		if (session.getAttribute("loginUserId") != null) {
			loginId = (String) session.getAttribute("loginUserId");
		}
		return loginId;
	}

	/**
	 * 日付形式チェック
	 *
	 * @param date
	 * @return
	 */
	public static boolean checkDateFormat(String dateStr, String dateFormat) {
		boolean stat = true;
		SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
		String checkDate = dateStr.replace("/", "");
		try {
			// if(checkDate.length() !=8){
			// logger.debug("checkDateFormat()：日付形式エラー ->" +dateStr);
			// return false;
			// }
			Calendar cal1 = Calendar.getInstance();

			// setLenientメソッドでfalseを設定しています。
			cal1.setLenient(false);

			// setメソッドで存在しない日付2008年2月30日を設定しています。
			int year = Integer.valueOf(checkDate.substring(0, 4));
			int month = Integer.valueOf(checkDate.substring(4, 6)) - 1;
			int day = Integer.valueOf(checkDate.substring(6, 8));
			cal1.set(year, month, day);
			cal1.getTime();

			Date date = sdf.parse(checkDate);
			if (date == null) {
				logger.debug("checkDateFormat()：日付形式エラー ->" + dateStr);
				return false;
			}
		} catch (IllegalArgumentException e) {
			logger.error("checkDateFormat()：日付形式エラー ->" + dateStr);
			stat = false;

		} catch (ParseException e) {
			stat = false;
			logger.error("checkDateFormat()：日付形式エラー ->" + dateStr);
		}
		return stat;
	}

	/**
	 * 日付の妥当性チェックを行います。 指定した日付文字列（yyyy/MM/dd or yyyy-MM-dd）が
	 * カレンダーに存在するかどうかを返します。
	 *
	 * @param strDate
	 *            チェック対象の文字列
	 * @return 存在する日付の場合true
	 */
	public static boolean checkDate(String strDate) {
		if (strDate == null) {
			return false;
		}
		SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
		// 日付/時刻解析を厳密に行うかどうかを設定する。
		format.setLenient(false);
		try {
			format.parse(strDate);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public static boolean urlCheck(String url) {

		if (url == null) {
			return false;
		}

		try {
			Pattern patt = Pattern.compile(MATCH_URL);
			Matcher matcher = patt.matcher(url);
			return matcher.matches();
		} catch (RuntimeException e) {
			return false;
		}

	}

	/**
	 * 日付形式チェック
	 *
	 * @param date
	 * @return
	 */
	public static boolean checkDateFormat(String dateStr) {
		return checkDateFormat(dateStr, "yyyyMMdd");
	}

	public static String trimUni(String s) {
		int len = s.length();
		int st = 0;
		char[] val = s.toCharArray();

		while (st < len && (val[st] <= ' ' || val[st] == '　')) {
			st++;
		}
		while (st < len && (val[len - 1] <= ' ' || val[len - 1] == '　')) {
			len--;
		}

		if (st > 0 || len < s.length()) {
			return s.substring(st, len);
		}

		return s;
	}
}
